#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/slab.h>

#define DEVICE_NAME "myworkdev"

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("User → Driver → Workqueue 参数传递示例");

static int major;
static struct workqueue_struct *my_wq;

// 与用户空间一致的结构体
struct user_data {
    int id;
    char msg[64];
};

// 内核侧封装的工作数据结构
struct my_work_data {
    struct work_struct work;
    struct user_data data;
};

// workqueue 处理函数
static void work_handler(struct work_struct *work)
{
    struct my_work_data *wdata = container_of(work, struct my_work_data, work);

    printk(KERN_INFO "[workqueue] Received from user: id=%d, msg=%s\n",
           wdata->data.id, wdata->data.msg);

    kfree(wdata); // 清理内存
}

// file_operations.write 实现
static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    struct my_work_data *wdata;

    if (count != sizeof(struct user_data))
        return -EINVAL;

    wdata = kmalloc(sizeof(struct my_work_data), GFP_KERNEL);
    if (!wdata)
        return -ENOMEM;

    if (copy_from_user(&wdata->data, buf, sizeof(struct user_data))) {
        kfree(wdata);
        return -EFAULT;
    }

    INIT_WORK(&wdata->work, work_handler);
    queue_work(my_wq, &wdata->work);

    return count;
}

// 其他 file_operations 接口
static int my_open(struct inode *inode, struct file *file) { return 0; }
static int my_release(struct inode *inode, struct file *file) { return 0; }

static const struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .write = my_write,
};

// 模块加载
static int __init my_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &my_fops);
    if (major < 0)
        return major;

    my_wq = create_workqueue("user_param_wq");

    printk(KERN_INFO "myworkdev loaded. Major: %d\n", major);
    return 0;
}

// 模块卸载
static void __exit my_exit(void)
{
    flush_workqueue(my_wq);
    destroy_workqueue(my_wq);
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "myworkdev unloaded.\n");
}

module_init(my_init);
module_exit(my_exit);
